#include <plib.h>
#include "Bike_Display_HW_Profile.h"
#include "Bike_Display_I2C_Functions.h"
#include <p32xxxx.h>



/*******************************************************************************
  Function:
    BOOL StartTransfer( BOOL restart )

  Summary:
    Starts (or restarts) a transfer to/from the EEPROM.

  Description:
    This routine starts (or restarts) a transfer to/from the EEPROM, waiting (in
    a blocking loop) until the start (or re-start) condition has completed.

  Precondition:
    The I2C module must have been initialized.

  Parameters:
    restart - If FALSE, send a "Start" condition
            - If TRUE, send a "Restart" condition
    
  Returns:
    TRUE    - If successful
    FALSE   - If a collision occured during Start signaling
    
  Example:
    <code>
    StartTransfer(FALSE);
    </code>

  Remarks:
    This is a blocking routine that waits for the bus to be idle and the Start
    (or Restart) signal to complete.
  *****************************************************************************/

BOOL StartTransfer( BOOL restart )
{
    I2C_STATUS  status;

    // Send the Start (or Restart) signal
    if(restart)
    {
        I2CRepeatStart(LED_DRIVER_I2C_BUS);
    }
    else
    {
        // Wait for the bus to be idle, then start the transfer
        while( !I2CBusIsIdle(LED_DRIVER_I2C_BUS) );

        if(I2CStart(LED_DRIVER_I2C_BUS) != I2C_SUCCESS)
        {
            DBPRINTF("Error: Bus collision during transfer Start\n");
            return FALSE;
        }
    }

    // Wait for the signal to complete
    do
    {
        status = I2CGetStatus(LED_DRIVER_I2C_BUS);

    } while ( !(status & I2C_START) );

    return TRUE;
}



/*******************************************************************************
  Function:
    void StopTransfer( void )

  Summary:
    Stops a transfer to/from the EEPROM.

  Description:
    This routine Stops a transfer to/from the EEPROM, waiting (in a 
    blocking loop) until the Stop condition has completed.

  Precondition:
    The I2C module must have been initialized & a transfer started.

  Parameters:
    None.
    
  Returns:
    None.
    
  Example:
    <code>
    StopTransfer();
    </code>

  Remarks:
    This is a blocking routine that waits for the Stop signal to complete.
  *****************************************************************************/

void StopTransfer( void )
{
    I2C_STATUS  status;

    // Send the Stop signal
    I2CStop(LED_DRIVER_I2C_BUS);

    // Wait for the signal to complete
    do
    {
        status = I2CGetStatus(LED_DRIVER_I2C_BUS);

    } while ( !(status & I2C_STOP) );
}



//NXP PCA9635 library

//Driver Software Reset
void PCA9635_Reset(UINT8 address)
{
    I2CData[0] = 0x06;           
    int ack = 	TransmitOneByte(I2CData[0]); 
    if(!ack);
    else return;
    I2CData[0] = 0xA5;
    I2CData[1] = 0x5A;
    I2C_write(address, I2CData, 2);
}

void PCA9635_Init(void)
{
	char 	i;
	UINT32	actualClock;
	UINT8	brightness = 0x80;

    //Enable both PCA9635 chips
	PORTClearBits(IOPORT_D, I2CLEDEnable);

 	//Set the I2C baudrate
    actualClock = I2CSetFrequency(LED_DRIVER_I2C_BUS, GetPeripheralClock(), I2C_CLOCK_FREQ);
    if ( abs(actualClock-I2C_CLOCK_FREQ) > I2C_CLOCK_FREQ/10 )
    {
        DBPRINTF("Error: I2C1 clock frequency (%u) error exceeds 10%%.\n", (unsigned)actualClock);
    }
	else
    {
        DBPRINTF("I2C1 clock frequency (%u) .\n", (unsigned)actualClock);
    }

	//Enable the I2C bus
    I2CEnable(LED_DRIVER_I2C_BUS, TRUE);

	//Set both chips up for writing 7 bit address + 0 for write
    I2C_FORMAT_7_BIT_ADDRESS(PCA9635Addr1, LED_DRIVER_ADDRESS_1, I2C_WRITE);
	I2C_FORMAT_7_BIT_ADDRESS(PCA9635Addr2, LED_DRIVER_ADDRESS_2, I2C_WRITE);

	for (LED_Driver_Chip = 1; LED_Driver_Chip < 3; LED_Driver_Chip++)
	{
		if (LED_Driver_Chip == 1)
		{
			LED_Driver_Chip_Address = PCA9635Addr1.byte;
		}
		else if (LED_Driver_Chip == 2)
		{
			LED_Driver_Chip_Address = PCA9635Addr2.byte;
		}		
		else 
		{
			DBPRINTF("LED driver chip number is (%u) .\n", LED_Driver_Chip);
		}		
		//I2CData[0] = LED_Driver_Chip_Address;	// select chip via address
	    I2CData[0] = 0x80;              		// control reg 1set to auto increment from addr 00 to 1B
	    I2CData[1] = 0x80;              		// start addr 00 - control reg 1 write same again 
	    I2CData[2] = 0x02;              		// control reg 2 - use OE/ pin
	    I2CData[3] = 0x00;              		// Set brightness for LED 1
	    I2CData[4] = 0x00;              		// Set brightness for LED 2
	    I2CData[5] = 0x00;              		// Set brightness for LED 3
	    I2CData[6] = 0x00;              		// Set brightness for LED 4
	    I2CData[7] = 0x00;              		// Set brightness for LED 5
	    I2CData[8] = 0x00;              		// Set brightness for LED 6
	    I2CData[9] = 0x00;              		// Set brightness for LED 7
	    I2CData[10] = 0x00;              		// Set brightness for LED 8
	    I2CData[11] = 0x00;              		// Set brightness for LED 9
	    I2CData[12] = 0x00;              		// Set brightness for LED 10
	    I2CData[13] = 0x00;              		// Set brightness for LED 11
	    I2CData[14] = 0x00;              		// Set brightness for LED 12
	    I2CData[15] = 0x00;              		// Set brightness for LED 13
	    I2CData[16] = 0x00;              		// Set brightness for LED 14
	    I2CData[17] = 0x00;              		// Set brightness for LED 15
	    I2CData[18] = 0x00;              		// Set brightness for LED 16
	    I2CData[19] = 0xFF;              		// Group duty cycle
	    I2CData[20] = 0x00;              		// Group freq
	    I2CData[21] = 0xAA;              		// out 0-4 set to PWM
	    I2CData[22] = 0xAA;              		// out 5-8 set to PWM
	    I2CData[23] = 0xAA;              		// out 9-12 set to PWM
	    I2CData[24] = 0xAA;              		// out 13-16 set to PWM
	
		I2C_write(LED_Driver_Chip_Address, I2CData, 25);
	}	
}

void PCA9635_Set_Address(UINT8 address)
{
 	if (address == 1)
	{
		LED_Driver_Chip_Address = PCA9635Addr1.byte;
	}
	if (address == 2)
	{
		LED_Driver_Chip_Address = PCA9635Addr2.byte;
	}		
	I2CData[0] = LED_Driver_Chip_Address;	// select chip via address
	
	I2C_write(address, I2CData, 1);
}

//Single LED On
void PCA9635_LED_On(char led)
{
	if (led <= 16)
    {
	    LED_Driver_Chip_Address = PCA9635Addr1.byte;
	    I2CData[0] = led + 1;     //led position      
    	I2CData[1] = LED_Brightness;        //brightness value
    	I2C_write(LED_Driver_Chip_Address, I2CData, 2);    
	}
	else if	(led <= 32)
	{
		LED_Driver_Chip_Address = PCA9635Addr2.byte;
	    I2CData[0] = led - 15;     //led position      
    	I2CData[1] = LED_Brightness;        //brightness value
    	I2C_write(LED_Driver_Chip_Address, I2CData, 2);    
	}	    
    else
    {
	   DBPRINTF("LED number is out of range,(%u) .\n", led);
	} 
}

//Single LED Off
void PCA9635_LED_Off(char led)
{
	if (led <= 16)
    {
	    LED_Driver_Chip_Address = PCA9635Addr1.byte;
	    I2CData[0] = led + 1;     //led position      
    	I2CData[1] = 0x00;        //brightness value
    	I2C_write(LED_Driver_Chip_Address, I2CData, 2);    
	}
	else if	(led <= 32)
	{
		LED_Driver_Chip_Address = PCA9635Addr2.byte;
	    I2CData[0] = led - 15;     //led position      
    	I2CData[1] = 0x00;        //brightness value
    	I2C_write(LED_Driver_Chip_Address, I2CData, 2);    
	}	    
    else
    {
	   DBPRINTF("LED number is out of range,(%u) .\n", led);
	} 
}

//Brightness control for single or all LEDs
void PCA9635_Brightness(char led, char value)
{
	char allLeds;
	
	LED_Brightness = value;

	for (LED_Driver_Chip = 1; LED_Driver_Chip < 3; LED_Driver_Chip++)
	{
		if (LED_Driver_Chip == 1)
		{
			LED_Driver_Chip_Address = PCA9635Addr1.byte;
		}
		else if (LED_Driver_Chip == 2)
		{
			LED_Driver_Chip_Address = PCA9635Addr2.byte;
		}		
		else 
		{
			DBPRINTF("LED driver chip number is (%u) .\n", LED_Driver_Chip);
		}
				
	    if(led == (char) ALL)
	    {
	        for(allLeds=0x02; allLeds<0x12; allLeds++)
	        {
	            I2CData[0] = allLeds;           
	            I2CData[1] = LED_Brightness; 
	            I2C_write(LED_Driver_Chip_Address, I2CData, 2);
	        }
	    }
	    else
	    {
	        I2CData[0] = led + 1;           
	        I2CData[1] = LED_Brightness; 
	        I2C_write(LED_Driver_Chip_Address, I2CData, 2);
	    }
	}
}


//Write all data to I2C address
int I2C_write(UINT8 address, UINT8 I2CData[], int size)
{
	int		Index;
    BOOL	Acknowledged;
    BOOL	Success = TRUE;

	// Start the transfer to write data to the PCA9635
    if( !StartTransfer(FALSE) )
    {
        while(1);
    }

	TransmitOneByte(address);

    // Transmit all data
    Index = 0;
    while( Success && (Index < size) )
    {
        // Transmit a byte
	    if (TransmitOneByte(I2CData[Index]))
        {
            // Advance to the next byte
            Index++;

            // Verify that the byte was acknowledged
            if(!I2CByteWasAcknowledged(LED_DRIVER_I2C_BUS))
            {
                DBPRINTF("Error: Sent byte was not acknowledged\n");
                Success = FALSE;
            }
        }
        else
        {
            Success = FALSE;
        }
    }

    // End the transfer (hang here if an error occured)
    StopTransfer();
    if(!Success)
    {
        while(1);
    }

	return 1;
}

/*******************************************************************************
  Function:
    BOOL TransmitOneByte( UINT8 data )

  Summary:
    This transmits one byte to the EEPROM.

  Description:
    This transmits one byte to the EEPROM, and reports errors for any bus
    collisions.

  Precondition:
    The transfer must have been previously started.

  Parameters:
    data    - Data byte to transmit

  Returns:
    TRUE    - Data was sent successfully
    FALSE   - A bus collision occured

  Example:
    <code>
    TransmitOneByte(0xAA);
    </code>

  Remarks:
    This is a blocking routine that waits for the transmission to complete.
  *****************************************************************************/

BOOL TransmitOneByte(UINT8 data)
{
    // Wait for the transmitter to be ready
    while(!I2CTransmitterIsReady(LED_DRIVER_I2C_BUS));

    // Transmit the byte
    if(I2CSendByte(LED_DRIVER_I2C_BUS, data) == I2C_MASTER_BUS_COLLISION)
    {
        DBPRINTF("Error: I2C Master Bus Collision\n");
        return FALSE;
    }

    // Wait for the transmission to finish
    while(!I2CTransmissionHasCompleted(LED_DRIVER_I2C_BUS));

    return TRUE;
}

